using System;
using System.Reflection;
using System.Runtime.Serialization;

namespace SharpCore.NullableTypes
{
	/// <summary>
	/// Wraps the <see cref="System.UInt16"/> value type in an object so that references can be null.
	/// </summary>
	[Serializable]
	public sealed class NullableUShort : INullable
	{
		/// <summary>
		/// The <see cref="System.UInt16"/> value this instance represents.
		/// </summary>
		private ushort ushortValue;

		/// <summary>
		/// Initializes a new instance of the NullableUShort class.
		/// </summary>
		public NullableUShort()
		{
			this.ushortValue = 0;
		}

		/// <summary>
		/// Initializes a new instance of the NullableUShort class with a <see cref="System.UInt16"/> value.
		/// </summary>
		/// <param name="ushortValue">The <see cref="System.UInt16"/> that this instance represents.</param>
		public NullableUShort(ushort ushortValue)
		{
			this.ushortValue = ushortValue;
		}

		/// <summary>
		/// Initializes a new instance of the NullableUShort class with a <see cref="System.String"/> that represents a <see cref="System.UInt16"/>.
		/// </summary>
		/// <param name="ushortValue">The string representation of a <see cref="System.UInt16"/> value.</param>
		public NullableUShort(string ushortValue)
		{
			this.ushortValue = UInt16.Parse(ushortValue);
		}

		/// <summary>
		/// Creates a NullableUInt16 that is serializable with the specified SerializationInfo and StreamingContext.
		/// </summary>
		/// <param name="info">A SerializationInfo object containing the information required to serialize the NullableUInt16.</param>
		/// <param name="context">A StreamingContext object containing the source and destination of the serialized stream associated with the NullableUInt16.</param>
		private NullableUShort(SerializationInfo info, StreamingContext context)
		{
			this.ushortValue = (ushort) info.GetUInt16("Value");
		}

		/// <summary>
		/// Returns the data needed to serialize the NullableUShort.
		/// </summary>
		/// <param name="info">A SerializationInfo object containing the information required to serialize the NullableUShort.</param>
		/// <param name="context">A StreamingContext object containing the source and destination of the serialized stream associated with the NullableUShort.</param>
		void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
		{
			info.AddValue("Value", ushortValue);
		}

		/// <summary>
		/// Gets or Sets the <see cref="System.UInt16"/> value this instance represents.
		/// </summary>
		public ushort UShortValue
		{
			get { return ushortValue; }
			set { ushortValue = value; }
		}

		/// <summary>
		/// Converts a <see cref="System.UInt16"/> to a NullableUShort.
		/// </summary>
		/// <param name="ushortValue">The <see cref="System.UInt16"/> to be converted.</param>
		/// <returns>A NullableUShort instance that wraps the <see cref="System.UInt16"/> value.</returns>
		public static implicit operator NullableUShort(ushort ushortValue)
		{
			return new NullableUShort(ushortValue);
		}

		/// <summary>
		/// Checks two NullableUShort instances for equality.
		/// </summary>
		/// <param name="a">A NullableUShort instance to test for equality.</param>
		/// <param name="b">A NullableUShort instance to test for equality.</param>
		/// <returns><b>true</b> if both instances are the same; otherwise <b>false</b>.</returns>
		public static bool operator ==(NullableUShort a, NullableUShort b)
		{
			if (a == null && b == null)
			{
				return true;
			}
			else if (a == null || b == null)
			{
				return false;
			}
			else
			{
				return (a.UShortValue == b.UShortValue);
			}
		}

		/// <summary>
		/// Checks two NullableUShort instances for inequality.
		/// </summary>
		/// <param name="a">A NullableUShort instance to test for inequality.</param>
		/// <param name="b">A NullableUShort instance to test for inequality.</param>
		/// <returns><b>true</b> if each instance is different; otherwise <b>false</b>.</returns>
		public static bool operator !=(NullableUShort a, NullableUShort b)
		{
			if (a == null && b == null)
			{
				return false;
			}
			else if (a == null || b == null)
			{
				return true;
			}
			else
			{
				return (a.UShortValue != b.UShortValue);
			}
		}

		/// <summary>
		/// Compares this instance to a specified object and returns an indication of their relative values.
		/// </summary>
		/// <param name="obj">An object to compare to this instance, or a null reference.</param>
		/// <returns>A signed integer that indicates the relative order of this instance and obj.</returns>
		public int CompareTo(object obj)
		{
			if (obj == null)
			{
				return 1;
			}
			else if (obj is NullableUShort)
			{
				NullableUShort nullableUShort = obj as NullableUShort;
				return ushortValue.CompareTo(nullableUShort.UShortValue);
			}
			else if (obj is ushort)
			{
				return ushortValue.CompareTo(obj);
			}
			else
			{
				throw new ArgumentException("The specified object was neither a ushort nor a NullableUShort instance.");
			}
		}

		/// <summary>
		/// Returns a value indicating whether this instance is equal to a specified object.
		/// </summary>
		/// <param name="obj">An object to compare to this instance.</param>
		/// <returns><b>true</b> if obj is a NullableUShort and has the same value as this instance; otherwise, <b>false</b>.</returns>
		public override bool Equals(object obj)
		{
			return ushortValue.Equals(obj);
		}

		/// <summary>
		/// Returns the hash code for this instance.
		/// </summary>
		/// <returns>A hash code for the current NullableUShort.</returns>
		public override int GetHashCode()
		{
			return ushortValue.GetHashCode();
		}

		/// <summary>
		/// Converts the value of this instance to its equivalent string representation.
		/// </summary>
		/// <returns>The string representation of the value of this instance.</returns>
		public override string ToString()
		{
			return ushortValue.ToString();
		}
	}
}
